#include "list.h"
#include "BST.h"

namespace STLData {

    list::list(std::string filename, std::string filepath) {

        char input = 0;
        std::string headerStruct;
        int i=0;

        dtstName = filename;
        dtstPath = filepath;
        std::string totalName = filepath + filename;
        dtstFile.open(totalName.c_str(), std::fstream::in | std::fstream::out);
        dtstFile.seekg(0, std::ios::beg);
        dtstFile.seekp(0, std::ios::beg);
        headerStruct.clear();
        while (input != '\n') {
            dtstFile >> input;
            headerStruct += input;
            if (input == '\n' ) {
                headerStruct[i] = '\0';
            }
        }
        headerSize = headerStruct.size()*sizeof(char);

        std::string fieldName;
        std::string fieldType;
        std::string lineTxt;
        int j;
        for (i=0; i<headerStruct.size();i++) {
            if (headerStruct[i] == '|') {
                int l = fieldType.size() - 2;
                std::string strl;
                if (l>0) {
                    strl = fieldType.substr(2, fieldType.size() - 2);
                    l = StringToNumber<int>(strl);
                } else {
                    l = 0;
                }
                headerValue.createField(fieldName, fieldType[0], l);
                j = 0;
                fieldName.clear();
                fieldType.clear();
            } else if (headerStruct[i] == ' ') {
                fieldName = fieldType;
                j = 0;
                fieldType.clear();
            } else {
                fieldType[j]=headerStruct[i];
                j++;
            }
        }
        std::streampos posIni = dtstFile.tellg();
        dtstFile >> input;

        while (input != '\n') {
            lineTxt += input;
            dtstFile >> input;
        }
        lineSize = (int) lineTxt.size() + 1;
        dtstFile.seekg(posIni);
        readLine();
    }

    list::list(dataLine variables, std::string dataSetName,
                std::string dataSetPath) {

        std::string headerStruct;
        int i;
        std::vector<std::string> headerDefs;

        dtstName = dataSetName;
        dtstPath = dataSetPath;
        std::string totalName = dataSetPath + dataSetName;
        dtstFile.open(totalName.c_str(), std::fstream::in | std::fstream::out);
        dtstFile.seekg(0, std::ios::beg);
        dtstFile.seekp(0, std::ios::beg);
        headerStruct.clear();
        headerValue = variables;

        for (i=0;i<=headerValue.maxIndex;i++) {
        }

    }

    bool list::readLine(){

        char * input;
        input = (char *) malloc(lineSize*sizeof(char));
        if (nextPos!=dtstFile.tellg())
            dtstFile.seekg(nextPos);
        presentLinePos = nextPos;
        if (!(dtstFile.getline(input, lineSize*sizeof(char)).eof())) {
            nextPos = dtstFile.tellg();
            lineCoded.assign(input);
            decodeLine(lineCoded);
            return true;
        } else {
     //       presentLinePos = dtstFile.end;
     //       nextPos = dtstFile.beg;
            return false;
        }
    }

    list& list::operator++() {
        readLine();
        return *this;
    }

    list& list::operator++(int) {
        ++(*this);
        return *this;
    }


    bool list::decodeLine(const std::string lineString) {

        std::string encoded;
        std::string decoded;
        std::istringstream iss;
        int j=0;
        double *d;
        long *l;

        iss.str(lineString);
        while(std::getline(iss, encoded, '|')) {
            if (encoded.size() != 0) {
                decoded = base64_decode(encoded);
                switch (headerValue.lineValues[j].type()) {
                    case 'T':
                        headerValue.lineValues[j] = decoded;
                        break;
                    case 'R':
                        d = reinterpret_cast<double*>((char *)decoded.c_str());
                        headerValue.lineValues[j] = *d;
                        break;
                    case 'I':
                        l = reinterpret_cast<long*>((char *)decoded.c_str());
                        headerValue.lineValues[j] = *l;
                        break;
                }
            }
            j++;
            encoded.clear();
            decoded.clear();
        }
        return true;
    }

    std::string list::codeHeader() {

        std::string fileEncoded, encodingField;
        int i;

        fileEncoded += '|';
        for (i=0;i<=headerValue.maxIndex;i++) {
            std::string buff;
            encodingField.clear();
            switch (headerValue.lineValues[i].type()) {
                case 'T':
                    buff = (std::string) headerValue.lineValues[i];
                    while (buff.size()<headerValue.lineValues[i].length()) {
                        buff += ' ';
                    }
                    encodingField = base64_encode(reinterpret_cast<const unsigned char*>(buff.c_str()), buff.length());
                    break;
                case 'R':
                    double valR;
                    valR = (double) headerValue.lineValues[i];
                    encodingField = base64_encode(reinterpret_cast<const unsigned char*>(&valR), sizeof(double));
                    break;
                case 'I':
                    long valI;
                    valI = (long) headerValue.lineValues[i];
                    encodingField = base64_encode(reinterpret_cast<const unsigned char*>(&valI), sizeof(long));
                    break;
            }
            fileEncoded += encodingField;
            fileEncoded += '|';
        }
        while (fileEncoded.size() < lineSize -1) {
            fileEncoded += ' ';
        }
        if (fileEncoded.size() > lineSize -1) {
            fileEncoded = fileEncoded.substr(0, lineSize -2);
            fileEncoded += '|';
        }
        lineCoded = fileEncoded;

        return fileEncoded;
    }

    bool list::modifyLine() {

        lineCoded = codeHeader();
        if (presentLinePos != dtstFile.tellp())
            dtstFile.seekp(presentLinePos);
        dtstFile << lineCoded << std::endl;
        if (nextPos!=dtstFile.tellp())
            nextPos=dtstFile.tellp();

        return true;
    }

    bool list::appendLine() {

        dtstFile.seekp(0, std::ios::end);
        codeHeader();
        dtstFile << lineCoded << std::endl;
        return true;
    }

    bool list::begin() {

        dtstFile.seekg(headerSize, std::ios::beg);
        nextPos = dtstFile.tellg();
        return readLine();

    }

    bool list::end() {

        dtstFile.seekg(lineSize, std::ios::end);
        nextPos = dtstFile.tellg();
        return readLine();

    }

    bool list::sort(std::vector<std::string> sortFields, std::vector<std::string> sortOrder) {

        int max = 100;
        BST tree;
        BNode::Comparable buffer;
        std::vector<std::fstream> files;
        std::ostringstream convert;

        int i;

        std::string pattern("/tmp/sort");
        std::string totalName;

        for (i=1;i<sortFields.size();i++) {
            buffer.fields.createField(sortFields[i],
                headerValue[sortFields[i]].type(), headerValue[sortFields[i]].length());
        }
        begin();
        buffer.fields.corresponding(headerValue);
        buffer.fileLine = lineCoded;
        tree.setComp(sortOrder);
        tree.insert(buffer);
        int j = 0;
        bool repeatLoop = true;
        while (repeatLoop) {
            for (i=2;i<max;i++) {
                if (readLine()) {
                    buffer.fields.corresponding(headerValue);
                    buffer.fileLine = lineCoded;
                    tree.insert(buffer);
                } else {
                    repeatLoop = false;
                    break;
                }
            }

            convert.str("");
            convert.fill('0');
            convert.width(4);
            convert << j;
            totalName = pattern + convert.str();
            files[j].open(totalName.c_str(), std::fstream::in | std::fstream::out);
            tree.setFile(files[j]);
            tree.inorderTree();
            j = j+1;
            tree.makeEmpty();

        }

        char * input;
        std::string lineRead;

        input = (char *) malloc(lineSize*sizeof(char));

        for (i=0; i<=j; i++) {
            files[i].getline(input, lineSize*sizeof(char));
            lineRead.assign(input);
            decodeLine(lineRead);
            buffer.fields.corresponding(headerValue);
            buffer.fileLine = lineCoded;
            buffer.filenum = i;
            tree.insert(buffer);
        }

        std::fstream out;
        totalName = "/tmp/" + dtstName + "9999";
        out.open(totalName.c_str(), std::fstream::in | std::fstream::out);
        out.seekg(0, std::ios::beg);
        out.seekp(0, std::ios::beg);
        dtstFile.seekg(0, std::ios::beg);
        dtstFile.getline(input, headerSize*sizeof(char));
        lineRead.assign(input);
        out << lineRead << std::endl;
        buffer = tree.findMin();
        while (buffer.fileLine!="NaN") {
            out << buffer.fileLine << std::endl;
            if (!files[buffer.filenum].eof()) {
                std::getline(files[buffer.filenum], lineRead);
                files[buffer.filenum].getline(input, lineSize*sizeof(char));
                lineRead.assign(input);
                decodeLine(lineRead);
                buffer.fields.corresponding(headerValue);
                buffer.fileLine = lineCoded;
                buffer.filenum = buffer.filenum;
                tree.insert(buffer);
            }
            buffer = tree.findMin();
        }

        return true;

    }


}
